﻿@page "/Nginx"
@using Tewr.Blazor.FileReader
@inject IFileReaderService fileReaderService;
@inject IToolCenter toolCenter
@inject INodeService iNodeService





<Form Model="@formModel"
      LabelColSpan="8"
      WrapperColSpan="10"
      OnFinish="OnFinish"
      Size="@AntSizeLDSType.Default">

    <FormItem Label="站点名称">
        <Input @bind-Value="@context.Title" Placeholder="Gateway website title" />
    </FormItem>

    <FormItem Label="协议头">
        <RadioGroup @bind-Value="@context.Schema">
            <Radio RadioButton Value='("http")'>http</Radio>
            <Radio RadioButton Value='("https")'>https</Radio>
        </RadioGroup>
    </FormItem>

    <FormItem Label="绑定域名">
        <Input @bind-Value="@context.Domain" Placeholder="例：baidu.com,www.baidu.com 多个用，分割" />
    </FormItem>

    @if (context.Schema == "https")
    {
        <FormItem Label="上传证书.key">
            <input id="fileKey" @ref=inputTypeFileElementKey type="file" accept=".key" style="margin-bottom:20px" />
            @if (string.IsNullOrWhiteSpace(progressKey))
            {
                <Alert Message="@($"请选择.key证书")" Type="@AlertType.Warning" />
            }
            else
            {
                <Alert Message="@progressKey" Type="@AlertType.Success" />
            }
        </FormItem>

        <FormItem Label="上传证书.pem">
            <input id="filePem" @ref=inputTypeFileElementPem type="file" accept=".pem" style="margin-bottom:20px" />
            @if (string.IsNullOrWhiteSpace(progressKey))
            {
                <Alert Message="@($"请选择.pem证书")" Type="@AlertType.Warning" />
            }
            else
            {
                <Alert Message="@progressPem" Type="@AlertType.Success" />
            }
        </FormItem>
    }



    <FormItem WrapperColOffset="8" WrapperColSpan="8">
        <Alert Message="请注意:"
               Description="绑定域名后，请确认该域名已解析至当前IP地址。 否则不生效。如果https证书不生效， 请检查是否上传错误或者已过期。"
               Type="@AlertType.Warning" />
    </FormItem>

    <FormItem WrapperColOffset="8" WrapperColSpan="8">
        <Button HtmlType="submit" Type="primary" Loading="context.IsSubmiting">
            Submit
        </Button>
    </FormItem>

    <div id="nginx-table" style="position:absolute; right:0px; top:0px; width:35%;">
        <PageHeader Class="site-page-header" Title="Service List" Subtitle="Please select the service item to use" />
        <Table @ref="tableFileApiIps" TItem="ServiceStatus" Context="fileItem" DataSource="fileApiIps" @bind-SelectedRows="context.fileApiServices" HidePagination="true">
            <Selection Key="@fileItem.Id" Type="checkbox" Disabled="fileItem.Status != 2" />
            <Column TData="string" Title="In use">
                <Checkbox Checked="fileItem.InUse" Disabled="true" />
            </Column>
            <Column Title="FileApi service" Field="@fileItem.Service" />
            <Column TData="int" Title="Status" Width="40%">
                @if (fileItem.Status == 2)
                {
                    <Badge Status="success" /> <i>Running</i>
                }
                else if (fileItem.Status == 3)
                {
                    <Badge Status="processing" /> <i>ReStarting</i>
                }
                else if (fileItem.Status == 4)
                {
                    <Badge Status="error" /> <i>Stopped</i>
                }
                else if (fileItem.Status == 5 || fileItem.Status == 6)
                {
                    <Badge Status="processing" /> <i>Not available</i>
                }
                else if (fileItem.Status == 11)
                {
                    <Badge Status="processing" /> <i>Waiting Stop Signal</i>
                }
                else if (fileItem.Status == 12)
                {
                    <Badge Status="processing" /> <i>Waiting Start Signal</i>
                }
            </Column>
        </Table>
        <br />
        <Table @ref="tableConnectorIps" TItem="ServiceStatus" Context="connectorItem" DataSource="connectorIps" @bind-SelectedRows="context.connectorServices" HidePagination="true">
            <Selection Key="@connectorItem.Id" Type="checkbox" Disabled="connectorItem.Status != 2" />
            <Column TData="string" Title="In use">
                <Checkbox Checked="connectorItem.InUse" Disabled="true" />
            </Column>
            <Column Title="Connector service" Field="@connectorItem.Service" />
            <Column TData="int" Title="Status" Width="40%">
                @if (connectorItem.Status == 2)
                {
                    <Badge Status="success" /> <i>Running</i>
                }
                else if (connectorItem.Status == 3)
                {
                    <Badge Status="processing" /> <i>ReStarting</i>
                }
                else if (connectorItem.Status == 4)
                {
                    <Badge Status="error" /> <i>Stopped</i>
                }
                else if (connectorItem.Status == 5 || connectorItem.Status == 6)
                {
                    <Badge Status="processing" /> <i>Not available</i>
                }
                else if (connectorItem.Status == 11)
                {
                    <Badge Status="processing" /> <i>Waiting Stop Signal</i>
                }
                else if (connectorItem.Status == 12)
                {
                    <Badge Status="processing" /> <i>Waiting Start Signal</i>
                }
            </Column>
        </Table>
    </div>



</Form>

<style>
    .ant-form-item-label {
        flex: 0 0 150px !important;
    }

    .ant-col-offset-8 {
        margin-left: 150px;
    }
    #nginx-table .ant-table-thead > tr > th {
        padding: 10px 16px;
    }
</style>

@code {
    ITable tableConnectorIps;
    ITable tableFileApiIps;

    private NginxRequest formModel = new NginxRequest();

    private ElementReference inputTypeFileElementKey;
    private ElementReference inputTypeFileElementPem;
    private string progressKeyFilePath;
    private string progressPemFilePath;
    private string nginxFilePath;
    private string nginxConfigPath;
    private string progressKey;
    private string progressPem;
    WindowsHelper windows;
    bool loading = false;

    List<ServiceStatus> connectorIps = new List<ServiceStatus>();
    List<ServiceStatus> fileApiIps = new List<ServiceStatus>();

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            windows = new WindowsHelper(jSRuntime);

            nginxFilePath = "/etc/nginx/conf.d/iToolGateway.conf";
            progressKeyFilePath = "/var/iTool/SSL_Gateway.key";
            progressPemFilePath = "/var/iTool/SSL_Gateway.pem";
            nginxConfigPath = "/var/iTool/.nginx_gateway";

#if DEBUG
        progressKeyFilePath = "D:\\iTool-Gateway-Blazor/SSL_Gateway.key";
        progressPemFilePath = "D:\\iTool-Gateway-Blazor/SSL_Gateway.pem";
        nginxFilePath = "D:\\iTool-Gateway-Blazor/iToolGateway.conf";
        nginxConfigPath = "D:\\iTool-Gateway-Blazor/.nginx_gateway";
#endif

            if (File.Exists(nginxConfigPath))
            {
                var value = File.ReadAllText(nginxConfigPath);
                formModel = value.TryToEntity<NginxRequest>();
            }

            if (File.Exists(progressKeyFilePath))
            {
                progressKey = ".key 证书文件已上传。";
            }

            if (File.Exists(progressPemFilePath))
            {
                progressPem = ".pem 证书文件已上传。";
            }

            await InvokeAsync(async () =>
            {
                var instances = await this.iNodeService.GetAllInstancesOfStorageAndConnector();
                var configure = await this.toolCenter.GetiToolConfigure();

            // iTool-Connector
            fileApiIps = instances.Where(item => item.Title == "iTool-FileApi").Select(item => new ServiceStatus
                {
                    Id = item.ServiceInstanceId.ToString(),
                    Service = $"\tserver    {item.NodeIP}:1996;",
                    Status = item.Status
                }).ToList();
                connectorIps = instances.Where(item => item.Title == "iTool-Connector").Select(item => new ServiceStatus
                {
                    Id = item.ServiceInstanceId.ToString(),
                    Service = $"\tserver    {item.NodeIP}:{configure.Port};",
                    Status = item.Status
                }).ToList();

                StateHasChanged();

                await windows.HideLoadingAsync();
                await windows.SetTitleAsync("Nginx");

                if (File.Exists(nginxConfigPath))
                {
                    var value = File.ReadAllText(nginxConfigPath);
                    formModel = value.TryToEntity<NginxRequest>();
                }

                await windows.ConsoleLogAsync(formModel);


                foreach (var connectorItem in connectorIps)
                {
                    connectorItem.InUse = formModel.connectorServices.Any(item => item.Service == connectorItem.Service);
                }

                foreach (var fileItem in fileApiIps)
                {
                    fileItem.InUse = formModel.fileApiServices.Any(item => item.Service == fileItem.Service);
                }

                StateHasChanged();
            });

            //tableConnectorIps.SetSelection(formModel.connectorServices.Select(x => x.Id).ToArray());
            //tableFileApiIps.SetSelection(formModel.fileApiServices.Select(x => x.Id).ToArray());
            //tableConnectorIps.SetSelection(formModel.connectorServices.Select(x => x.Id).ToArray());

        }

    }

    private async void OnFinish(EditContext editContext)
    {
        await windows.ConsoleLogAsync(formModel);

        if (formModel.Schema == "https")
        {
            var value = await windows.GetInputValueAsync("fileKey");
            if (string.IsNullOrWhiteSpace(value))
            {
                if (!File.Exists(progressKeyFilePath))
                {
                    var task = notice.Error(new NotificationConfig
                    {
                        Message = "请选择证书文件 .key"
                    });
                    return;
                }
            }
            else
            {
                await this.ReadFile(true, inputTypeFileElementKey);
            }

            value = await windows.GetInputValueAsync("filePem");
            if (string.IsNullOrWhiteSpace(value))
            {
                if (!File.Exists(progressPemFilePath))
                {
                    var task = notice.Error(new NotificationConfig
                    {
                        Message = "请选择证书文件 .pem"
                    });
                    return;
                }
            }
            else
            {
                await this.ReadFile(false, inputTypeFileElementPem);
            }
        }

        if (string.IsNullOrWhiteSpace(formModel.Domain))
        {
            var task = notice.Error(new NotificationConfig
            {
                Message = "域名不能为空"
            });
            return;
        }

        if (formModel.connectorServices.Count() == 0)
        {
            _ = notice.Error(new NotificationConfig
            {
                Message = "not find connector"
            });
            return;
        }

        if (formModel.fileApiServices.Count() == 0)
        {
            _ = notice.Error(new NotificationConfig
            {
                Message = "not find file server"
            });
            return;
        }

        formModel.IsSubmiting = true;
        _ = InvokeAsync(StateHasChanged);

        File.WriteAllText(nginxFilePath, this.BuildNginxConfig());
        var nginxTestValue = Shell.Excuter("sh ./nginxReload.sh");

        LoggerUtils.Write($"nginxTestValue：{nginxTestValue}");

        var isNginxTestSuccessful = !nginxTestValue.Contains("failed");

        await Task.Delay(1000);
        if (isNginxTestSuccessful)
        {

            formModel.IsSubmiting = false;
            File.WriteAllText(nginxConfigPath, formModel.TryToJson());
            var task = notice.Success(new NotificationConfig
            {
                Message = "操作成功",
                Description = "注：绑定域名后，请确认域名已解析至当前IP地址"
            });
        }
        else
        {
            formModel.IsSubmiting = false;
            var task = notice.Error(new NotificationConfig
            {
                Message = "操作失败",
                Description = nginxTestValue
            });
        }
        _ = InvokeAsync(StateHasChanged);
        await windows.ConsoleErrorAsync(this.formModel);
    }

    public async Task ReadFile(bool isKey, ElementReference elementReference)
    {
        var value = await windows.GetInputValueAsync("files");

        await windows.ConsoleLogAsync($"{value} - 文件后缀：{Path.GetExtension(value)}");
        await windows.ConsoleLogAsync($"{elementReference.Id}");

        foreach (var file in await fileReaderService.CreateReference(elementReference).EnumerateFilesAsync())
        {

            await using var fileStream = await file.OpenReadAsync();
            var buffer = new byte[200];
            var finalBuffer = new byte[fileStream.Length];
            int count;
            int totalCount = 0;
            while ((count = await fileStream.ReadAsync(buffer, 0, buffer.Length)) != 0)
            {
                Buffer.BlockCopy(buffer, 0, finalBuffer, totalCount, count);
                totalCount += count;
                if (isKey)
                {
                    progressKey = "文件上传中 " + (int)(totalCount * 100.0 / fileStream.Length) + "%";
                }
                else
                {
                    progressPem = "文件上传中 " + (int)(totalCount * 100.0 / fileStream.Length) + "%";
                }
                StateHasChanged();
            }

            using (FileStream fs = new FileStream(isKey ? progressKeyFilePath : progressPemFilePath, FileMode.Create, FileAccess.Write))
            {
                fs.Write(finalBuffer, 0, finalBuffer.Length);
            }

            StateHasChanged();
        }
    }

    private string BuildNginxConfig()
    {

        bool isssl = formModel.Schema == "https";
        string ssl443 = isssl ? "listen 443 ssl http2;" : string.Empty;
        string server_name = string.Join(' ', formModel.Domain.Replace("，", ",").Split(","));

        System.Text.StringBuilder sslStringBuilder = new System.Text.StringBuilder();
        if (isssl)
        {
            sslStringBuilder.AppendLine($"ssl_certificate {progressPemFilePath};");
            sslStringBuilder.AppendLine($"\tssl_certificate_key {progressKeyFilePath};");
            sslStringBuilder.AppendLine("\tssl_protocols TLSv1.1 TLSv1.2;");
            sslStringBuilder.AppendLine("\tssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;");
            sslStringBuilder.AppendLine("\tssl_prefer_server_ciphers on;");
            sslStringBuilder.AppendLine("\tssl_session_cache shared:SSL:10m;");
            sslStringBuilder.AppendLine("\tssl_session_timeout 10m;");
        }

        string nginx = $@"# 文件服务器 - 有限分配给响应最快的服务
upstream  iToolFiles {{
least_conn;
{string.Join('\n', formModel.fileApiServices.Select(item => item.Service))}
}}

# 连接器 - 优先分配给最少连接的服务
upstream  iToolClient {{
least_conn;
{string.Join('\n', formModel.connectorServices.Select(item => item.Service))}
}}

server {{
listen 80;
{ssl443}
server_name {server_name};
{sslStringBuilder}

location /storage {{
proxy_pass  http://iToolFiles/api/Storage;
proxy_http_version 1.1;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_set_header x-real-ip $remote_addr;
}}

location /connector {{
proxy_pass http://iToolClient;
proxy_http_version 1.1;
proxy_read_timeout 1200s;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_set_header x-real-ip $remote_addr;
proxy_cache_bypass $http_upgrade;
}}

location / {{
proxy_pass http://127.0.0.1:1994;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_set_header x-real-ip $remote_addr;
proxy_cache_bypass $http_upgrade;
}}
}}";

        return nginx;
    }
}